/*******************************************************************************
 * Copyright (c) 2011 Jovansonlee B. Cesar.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Jovansonlee B. Cesar - initial API and implementation
 ******************************************************************************/
package com.ivanceras.shared.db;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.ivanceras.shared.exception.DatabaseException;
import com.ivanceras.shared.exception.ModelDefinitionNotFoundException;
import com.ivanceras.shared.meta.ModelDefinitionProvider;

public class ModelDef{
	
	private String namespace;
	private String modelName;
	private String[] attributes;
	private String generatedAttribute;
	private String[] primaryAttributes;
	private String[] uniqueAttributes;
	private String[] dataTypes;
	private Object[] values;
	
	private String[] hasOne;
	private String[] hasOneForeignColumn; //local column of this model
	private String[] hasOneReferredColumn; // referred column from the referred table
	
	private String[] hasMany;
	private String[] hasManyForeignColumn; //local column
	private String[] hasManyReferredColumn; //referred column from the referred table
	
	private String[] hasManyMany;
	private String[] hasManyManyInvolvedModel; /** intermediatiary involved models to connect between many to many relationship of records **/
	private String[] hasManyManyInvolvedModelForeignColumn;
	private String[] hasManyManyInvolvedReferredColumn;
	
	
	private boolean caseSensitive;
	private boolean polymorphic;
	
	
	public ModelDef(String namespace, String modelName, String[] attributes, String generatedAttribute,
			String[] dataTypes, String[] primaryAttributes, String[] uniqueAttributes, 
			String[] hasOne, String[] hasOneForeignColumn, String[] hasOneReferredColumn,
			String[] hasMany, String[] hasManyForeignColumn, String[] hasManyReferredColumn,
			String[] hasManyMany,String[] hasManyManyInvolvedModel, String[] hasManyManyInvolvedModelForeignColumn, String[] hasManyManyInvolvedReferredColumn,
			boolean caseSensitive) {
		this.namespace = namespace;
		this.modelName = modelName;
		this.attributes = attributes;
		this.generatedAttribute = generatedAttribute;
		this.dataTypes = dataTypes;
		this.primaryAttributes = primaryAttributes;
		this.uniqueAttributes = uniqueAttributes;
		this.hasOne = hasOne;
		this.hasOneForeignColumn = hasOneForeignColumn;
		this.hasOneReferredColumn = hasOneReferredColumn;
		
		this.hasMany = hasMany;
		this.hasManyForeignColumn = hasManyForeignColumn;
		this.hasManyReferredColumn = hasManyReferredColumn;
		
		this.hasManyMany = hasManyMany;
		this.hasManyManyInvolvedModel = hasManyManyInvolvedModel;
		this.hasManyManyInvolvedModelForeignColumn = hasManyManyInvolvedModelForeignColumn;
		this.hasManyManyInvolvedReferredColumn = hasManyManyInvolvedReferredColumn;
		
		this.caseSensitive = caseSensitive;	
	}


	
	public ModelDef() {

	}



	public String getNamespace() {
		return namespace;
	}

	public String getModelName() {
		return modelName;
	}

	public String[] getAttributes() {
		return attributes;
	}

	public String getGeneratedAttribute(){
		return generatedAttribute;
	}
	
	
	public String[] getDataTypes() {
		return dataTypes;
	}

	
	public Object[] getValues() {
		return values;
	}

	
	public String[] getHasMany() {
		return hasMany;
	}

	
	public String[] getHasManyForeignColumn() {
		return hasManyForeignColumn;
	}
	
	
	public String[] getHasManyReferredColumn(){
		return hasManyReferredColumn;
	}

	
	public String[] getHasManyMany() {
		return hasManyMany;
	}

	
	
	
	public String[] getHasManyManyInvolvedModel() {
		return hasManyManyInvolvedModel;
	}

	
	public String[] getHasManyManyInvolvedModelForeignColumn() {
		return hasManyManyInvolvedModelForeignColumn;
	}
	
	
	public String[] getHasManyManyInvolvedReferredColumn(){
		return hasManyManyInvolvedReferredColumn;
	}

	
	public String[] getPrimaryAttributes() {
		return primaryAttributes;
	}

	
	public String[] getUniqueAttributes() {
		return uniqueAttributes;
	}

	
	public String[] getHasOne() {
		return hasOne;
	}

	
	public String[] getHasOneForeignColumn() {
		return hasOneForeignColumn;
	}
	
	
	public String[] getHasOneReferredColumn(){
		return hasOneReferredColumn;
	}

	
	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}

	
	public void setModelName(String table) {
		this.modelName = table;
	}

	
	public void setAttributes(String[] columns) {
		this.attributes = columns;
	}

	
	public void setDataTypes(String[] dataTypes) {
		this.dataTypes = dataTypes;
	}

	
	public void setValues(Object[] values) {
		this.values = values;
	}

	
	public void setPrimaryAttributes(String[] primaryColumns) {
		this.primaryAttributes = primaryColumns;
	}

	
	public void setUniqueAttributes(String[] uniqueColumns) {
		this.uniqueAttributes = uniqueColumns;
	}

	
	public void setHasMany(String[] hasMany) {
		this.hasMany = hasMany;
	}

	
	public void setHasManyForeignColumn(String[] hasManyForeignColumn) {
		this.hasManyForeignColumn = hasManyForeignColumn;
	}
	
	
	public void setHasManyReferredColumn(String[] hasManyReferredColumn) {
		this.hasManyReferredColumn = hasManyReferredColumn;
	}

	
	public void setHasManyMany(String[] hasManyMany) {
		this.hasManyMany = hasManyMany;
	}

	
	public void setHasManyManyInvolvedModel(String[] hasManyManyInvolvedModel) {
		this.hasManyManyInvolvedModel = hasManyManyInvolvedModel;
	}

	
	public void setHasManyManyForeignColumn(String[] hasManyManyForeignColumn) {
		this.hasManyManyInvolvedModelForeignColumn = hasManyManyForeignColumn;
	}
	
	
	public void setHasManyManyReferredColumn(String[] hasManyManyReferredColumn){
		this.hasManyManyInvolvedReferredColumn = hasManyManyReferredColumn;
	}

	
	public void setHasOne(String[] hasOne) {
		this.hasOne = hasOne;
	}
	
	
	public void setHasOneForeignColumn(String[] hasOneForeignColumn) {
		this.hasOneForeignColumn = hasOneForeignColumn;
	}
	
	
	public void setHasOneReferredColumn(String[] hasOneReferredColumn){
		this.hasOneReferredColumn = hasOneReferredColumn;
	}
	

	
	public String getPrimaryColumn(){
		if(this.primaryAttributes != null){
			return this.primaryAttributes[0];
		}
		return null;
	}
	
	
	public String[] getPrimaryColumns() {
		return getPrimaryAttributes();
	}

	
	public String getForeignKeyColumn(String modelName){
		//System.out.println("Searching of foreign key of "+modelName+" from table "+this.modelName);
		for(int i = 0; i < this.hasOne.length; i++){
			if(modelName.equalsIgnoreCase(this.hasOne[i])){
				return this.hasOneForeignColumn[i];
			}
		}
		return null;
	}
	
	
	/**
	 * get the foreign key column of this table definition from this refered table
	 */
	public String[] getForeignKeyColumns(String modelName) {
		List<String> foreignColumnList = new ArrayList<String>();
		for(int i = 0; i < this.hasOne.length; i++){
			if(modelName.equalsIgnoreCase(this.hasOne[i])){
				foreignColumnList.add(hasOneForeignColumn[i]);
			}
		}
		for(int j = 0; j < this.hasMany.length; j++){
			if(modelName.equalsIgnoreCase(hasMany[j])){
				foreignColumnList.add(hasManyReferredColumn[j]);
			}
		}
		if(foreignColumnList.size() == 0){
			return null;
		}
		return foreignColumnList.toArray(new String[foreignColumnList.size()]);
	}
	
	
	public String[] getLocalColumns(String modelName) {
		List<String> localColumnList = new ArrayList<String>();
		for(int i = 0; i < this.hasOne.length; i++){
			if(modelName.equalsIgnoreCase(this.hasOne[i])){
				localColumnList.add(hasOneReferredColumn[i]);
			}
		}
		for(int j = 0; j < this.hasMany.length; j++){
			if(modelName.equalsIgnoreCase(hasMany[j])){
				localColumnList.add(hasManyForeignColumn[j]);
			}
		}
		if(localColumnList.size() == 0){
			return null;
		}
		return localColumnList.toArray(new String[localColumnList.size()]);
	}

	
	public void setGeneratedAttribute(String generatedAttribute) {
		this.generatedAttribute = generatedAttribute;
	}

	
	public boolean isCaseSensitive() {
		return caseSensitive;
	}

	
	public void setCaseSensitive(boolean caseSensitive) {
		this.caseSensitive = caseSensitive;
	}
	
	
	public String toSummaryString(){
		StringBuffer sb = new StringBuffer();
		sb.append("model "+modelName+"{");
		sb.append("\n\tnamespace:"+namespace);
		sb.append("\n\tAttributes: ["+Arrays.asList(attributes)+"]");
		sb.append("\n\tdataTypes:"+(dataTypes!= null ? Arrays.asList(dataTypes):null));
		sb.append("\n\tgenerated:"+generatedAttribute);
		sb.append("\n\tprimary:"+(primaryAttributes != null? Arrays.asList(primaryAttributes): null));
		sb.append("\n\tuniques:"+(uniqueAttributes!= null ? Arrays.asList(uniqueAttributes): null));
		sb.append("\n\thasOne:"+Arrays.asList(hasOne));
		sb.append("\n\thasOneForeignColumn:"+(hasOneForeignColumn!=null? Arrays.asList(hasOneForeignColumn):null));
		sb.append("\n\thasOneReferredColumn:"+(hasOneReferredColumn!=null? Arrays.asList(hasOneReferredColumn):null));
		sb.append("\n\thasMany:"+(hasMany!=null?Arrays.asList(hasMany):null));
		sb.append("\n\thasManyForeignColumn:"+(hasManyForeignColumn!=null?Arrays.asList(hasManyForeignColumn):null));
		sb.append("\n\thasManyReferredColumn:"+(hasManyReferredColumn!=null?Arrays.asList(hasManyReferredColumn):null));
		sb.append("\n\tcaseSensitive:"+caseSensitive);
		sb.append("\n}");
		return sb.toString();
	}
	
	
	public String getReferencedTable(String column){
		for(int p = 0; p < hasOneForeignColumn.length; p++){
			System.out.println(hasOneForeignColumn[p]+" : "+hasOne[p]);
		}
		
		for(int l = 0; l < hasOneForeignColumn.length; l++){
			String refColumn = hasOneForeignColumn[l];
			if(refColumn.equalsIgnoreCase(column)){
				return hasOne[l];
			}
		}
		return null;
	}
	
	
	public boolean isPrimaryColumn(String column) {
		for(int i = 0; i < primaryAttributes.length; i++){
			String attr = primaryAttributes[i];
			if(attr.equals(column)){
				return true;
			}
		}
		return false;
	}

	
	public static void main(String[] args) throws ModelDefinitionNotFoundException, DatabaseException{
		EntityManager em = EMF.get();
		ModelDef modeldef = ModelDefinitionProvider.getDefinition("Ad_table");
		System.out.println(modeldef.toSummaryString());
	}



	public void setPolymorphic(boolean polymorphic) {
		this.polymorphic = polymorphic;
	}



	public boolean isPolymorphic() {
		return polymorphic;
	}
}
